# -*- mode: org; fill-column: 50; -*-
#+TITLE: Literate Programming Guide
#+AUTHOR: Zelphir Kaltstahl
#+STARTUP: content indent align inlineimages hideblocks entitiesplain nologdone nologreschedule nologredeadline nologrefile
#+TODO: TODO INPROGRESS | DONE
#+DATE: [2022-05-15 So]
#+LANGUAGE: en
#+PRIORITIES: A E E
#+OPTIONS: ^:{}
#+OPTIONS: H:10
#+OPTIONS: toc:2
#+OPTIONS: tags:t
#+OPTIONS: tasks:t
#+OPTIONS: H:6
#+OPTIONS: p:nil
#+OPTIONS: pri:t
#+OPTIONS: prop:nil
#+OPTIONS: todo:t
#+OPTIONS: stat:nil
#+OPTIONS: |:t
#+OPTIONS: inline:nil

* About
:PROPERTIES:
:custom_id: about
:END:

This section aims to provide the minimal steps required to get literate programming in org-mode working for GNU Guile.

* Setup
:PROPERTIES:
:custom_id: setup
:END:

** Installing Emacs
:PROPERTIES:
:custom_id: installing-emacs
:END:

We will use GNU Guix to create a reproducible environment, in which we will run Emacs, install Emacs packages and configure Emacs using Elisp code.

*** Reproducible Emacs using GNU Guix

**** Create manifest.scm

#+begin_src shell :results output drawer replace verbatim
  cat <<EOT > manifest.scm
  (specifications->manifest
   '("gnutls"
     "guile"
     "bash"
     "emacs"))
  EOT

  cat manifest.scm
#+end_src

#+RESULTS:
:results:
(specifications->manifest
 '("gnutls"
   "guile"
   "bash"
   "emacs"))
:end:

**** Create channels.scm

#+begin_src shell :results output drawer replace verbatim output :exports both
  guix describe --format='channels' > 'channels.scm'
  cat 'channels.scm'
#+end_src

#+RESULTS:
:results:
(list (channel
        (name 'guix)
        (url "https://git.savannah.gnu.org/git/guix.git")
        (branch "master")
        (commit
          "22d6e36005322cd641bc1107f3f04945e03478d5")
        (introduction
          (make-channel-introduction
            "9edb3f66fd807b096b48283debdcddccfea34bad"
            (openpgp-fingerprint
              "BBB0 2DDF 2CEA F6A8 0D1D  E643 A2A0 6DF2 A33A 54FA")))))
:end:

**** Install packages

#+begin_src shell :results output drawer replace verbatim
  command -V guile
  ls -al $(command -V guile)

  # compare to guile installed in the environment created via guix shell:

  guix time-machine \
       --channels="channels.scm" -- \
       shell \
       --cores=8 \
       --manifest="manifest.scm" -- \
       which guile && ls -al $(which guile)
#+end_src

#+RESULTS:
:results:
guile is /home/xiaolong/.guix-profile/bin/guile
lrwxrwxrwx 1 root root 65 Jan  1  1970 /home/xiaolong/.guix-profile/bin/guile -> /gnu/store/1jgcbdzx2ss6xv59w55g3kr3x4935dfb-guile-3.0.8/bin/guile
/gnu/store/abw05ikf9q0by8xp7g19hd7d7hj40cvi-profile/bin/guile
lrwxrwxrwx 1 root root 65 Jan  1  1970 /home/xiaolong/.guix-profile/bin/guile -> /gnu/store/1jgcbdzx2ss6xv59w55g3kr3x4935dfb-guile-3.0.8/bin/guile
:end:

**** Command for running Emacs inside reproducible environment

#+begin_src shell :results output
  guix time-machine \
       --channels="channels.scm" -- \
       shell \
       --cores=8 \
       --pure \
       --container \
       --network \
       --no-cwd \
       --preserve='TERM' \
       --manifest="manifest.scm" -- \
       emacs \
       --no-init-file \
       --no-site-file \
       --no-site-lisp \
       --no-splash \
       --no-x-resources \
       --no-desktop \
       --no-loadup \
       --no-window-system
#+end_src

Or with graphical window:

#+begin_src shell :results output
  guix time-machine \
       --channels="channels.scm" -- \
       shell \
       --cores=8 \
       --pure \
       --container \
       --network \
       --no-cwd \
       --share=/tmp/.X11-unix \
       --preserve='DISPLAY' \
       --preserve='TERM' \
       --manifest="manifest.scm" -- \
       emacs \
       --no-init-file \
       --no-site-file \
       --no-site-lisp \
       --no-splash \
       --no-x-resources \
       --no-desktop \
       --no-loadup
#+end_src

#+RESULTS:

** Installing required Emacs packages
:PROPERTIES:
:custom_id: install-emacs-packages
:END:

*** Add melpa-stable package repository

To install geiser, we need to add the package repository, from which we can pull it.

Run the following elisp code:

#+begin_src elisp
(require 'package)
;; (add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(add-to-list 'package-archives '("melpa-stable" . "https://stable.melpa.org/packages/") t)
(package-initialize)
#+end_src

*** Install org

=org= is already installed. Not sure how to tell Emacs, that it should instead install =org= from GNU Elpa, so do this manually:

+ ~M-x list-pack RET~
+ search for =org= from GNU Elpa
+ install =org=

*** Installing geiser-guile

As Elisp code:

#+begin_src elisp
(package-install 'geiser-guile)
#+end_src

** Configuring Emacs using Elisp code
:PROPERTIES:
:custom_id: configuring-emacs
:END:

The following Elisp code needs to be evaluated, to be able to evaluate org source blocks of Scheme code using GNU Guile:

#+begin_src elisp
  ;; Tell Emacs to use the command `guile` to evaluate Scheme
  ;; code.
  (setq scheme-program-name "guile")

  ;; The following enables source blocks of specific
  ;; programming languages to be executed. By default only
  ;; elisp is allowed to be executed. First we define an
  ;; association list of languages to booleans, which enable
  ;; or disable support for a language.
  (setq org-babel-load-languages
        '((scheme . t)))

  ;; Then we use org-babel-do-load-languages to actually tell
  ;; org babel, that the languages should be supported.
  (org-babel-do-load-languages
   'org-babel-load-languages
   '((scheme . t)))

  ;; The following defines a procedure, which determins, which
  ;; source blocks of programming languages can be run without
  ;; confirmation, depending on the language. The procedure
  ;; can then be used as org-confirm-babel-evaluate value.
  (defun xiaolong/org-confirm-babel-evaluate (lang body)
    (not
     (or (string= lang "scheme"))))

  ;; Set a procedure predicate for org babel, to determin, which languages can
  ;; be run without confirmation.
  (setq org-confirm-babel-evaluate 'xiaolong/org-confirm-babel-evaluate)
#+end_src

* Test
:PROPERTIES:
:custom_id: test
:END:

** Running geiser
:PROPERTIES:
:custom_id: running-geiser
:END:

Run geiser-guile using the following command:

#+begin_example
M-x run-geiser RET
#+end_example

Since only =geiser-guile= is installed, this should start a Geiser Guile REPL.

** Literate programming document
:PROPERTIES:
:custom_id: example-document
:END:

Now we should be all set to try some literate programming in Scheme.

Here is an example document:

#+begin_src shell :results output
  cat example.org
#+end_src

#+RESULTS:
#+begin_example
,#+NAME: defmember
,#+begin_src scheme :noweb yes :results none :exports code :eval never-export :language guile
(define member?
  (λ (a lat)
    (cond
     [(null? lat) #f]
     [else
      (or (eq? a (car lat))
          (member? a (cdr lat)))])))
,#+end_src

Lets test it:

,#+NAME: usemember
,#+begin_src scheme :noweb yes :results output :exports both :eval never-export
<<defmember>>

(display
 (simple-format
  #f "(member? 1 '(3 2 1)) -> ~a\n"
  (member? 1 '(3 2 1))))

(display
 (simple-format
  #f "(member? 1 '(3 2 4)) -> ~a\n"
  (member? 1 '(3 2 4))))
,#+end_src
#+end_example

1. Open a new =.org= file.
2. Paste the =example.org= document into the buffer of the =.org= file.
3. Place the cursor inside the =usemember= source block.
4. Input =C-c C-c= to evaluate the source block.

* Report
:PROPERTIES:
:custom_id: report
:END:

The above does not seem to work. The result of the example source block is merely the following:

#+begin_quote
#+RESULTS: usemember
: Geiser Interpreter produced no output
#+end_quote
